package com.simple.common.framework.autotask;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * @author yasud
 * @date 2021年04月02日 15:49
 * @description
 */
@Slf4j
public class Tasks {

    public static final Scheduled SCHEDULED = new Scheduled(new ScheduledThreadPoolExecutor(5, (r, executor) -> {
        executor.getQueue().offer(r);
    }));

    private static class Station {
        private Cache<Supplier, ScheduledFuture> cache = CacheBuilder.newBuilder()
                .build();

        public void put(Supplier r, ScheduledFuture sf) {
            cache.put(r, sf);
        }

        public ScheduledFuture get(Supplier r) {
            return cache.getIfPresent(r);
        }

        public void remove(Supplier r) {
            cache.invalidate(r);
        }
    }

    public static class Delay {
        private long initDelay;
        private long delay;

        private TimeUnit unit;

        public static final Delay DEFAULT = delay(10, 10, TimeUnit.SECONDS);

        private Delay(long initDelay, long delay, TimeUnit unit) {
            this.initDelay = initDelay;
            this.delay = delay;
            this.unit = unit;
        }

        public static Delay delay(long initDelay, long delay, TimeUnit unit) {
            return new Delay(initDelay, delay, unit);
        }
    }

    public static class Scheduled {
        private ScheduledExecutorService scheduled;
        private Station station = new Station();

        public Scheduled(ScheduledExecutorService scheduled) {
            this.scheduled = scheduled;
        }

        /**
         * 该方法在指定运行次数内， 如果supplier 得到为true 则退出该定时任务
         *
         * @param sp
         * @param times
         */
        public <R> void run(Supplier<R> sp, int times, Function<R, Boolean> fun, Delay delay) {
            LongAccumulator timess = new LongAccumulator((left, right) -> left - right, times);
            ScheduledFuture<?> scheduledFuture = scheduled.scheduleWithFixedDelay(() -> {
                if (timess.get() != 0) {
                    try {
                        log.info("任务{}第{}次执行", sp.toString(), timess.get());
                        R r = sp.get();
                        if (!fun.apply(r)) {
                            log.info("任务{}执行完毕取消", sp.toString());
                            ScheduledFuture sf = station.get(sp);
                            sf.cancel(true);
                            station.remove(sp);
                            return;
                        }
                    } catch (Exception ex) {
                        ex.printStackTrace();
                        log.info("任务{}执行异常取消", sp.toString());
                        ScheduledFuture sf = station.get(sp);
                        sf.cancel(true);
                        station.remove(sp);
                        return;
                    }
                    log.info("任务{}第{}次完毕", sp.toString(), timess.get());
                    timess.accumulate(1);
                } else {
                    log.info("任务{}执行完毕取消", sp.toString());
                    ScheduledFuture sf = station.get(sp);
                    sf.cancel(true);
                    station.remove(sp);
                }
            }, delay.initDelay, delay.delay, delay.unit);
            station.put(sp, scheduledFuture);
        }

        /**
         * 该方法在指定运行次数内，则退出该定时任务
         *
         * @param times
         */
        public <R> void run(Supplier<R> sp, int times, Delay delay) {
            LongAccumulator timess = new LongAccumulator((left, right) -> left - right, times);
            ScheduledFuture<?> scheduledFuture = scheduled.scheduleWithFixedDelay(() -> {
                if (timess.get() != 0) {
                    try {
                        log.info("任务{}第{}次执行", sp.toString(), timess.get());
                        R r = sp.get();
                    } catch (Exception ex) {
                        ex.printStackTrace();
                        log.info("任务{}执行异常取消", sp.toString());
                        ScheduledFuture sf = station.get(sp);
                        sf.cancel(true);
                        station.remove(sp);
                        return;
                    }
                    log.info("任务{}第{}次完毕", sp.toString(), timess.get());
                    timess.accumulate(1);
                } else {
                    log.info("任务{}执行完毕取消", sp.toString());
                    ScheduledFuture sf = station.get(sp);
                    sf.cancel(true);
                    station.remove(sp);
                }
            }, delay.initDelay, delay.delay, delay.unit);
            station.put(sp, scheduledFuture);
        }

        /**
         * 如果supplier 得到为true 则退出该定时任务
         */
        public <R> void run(Supplier<R> sp, Function<R, Boolean> fun,  Delay delay) {
            ScheduledFuture<?> scheduledFuture = scheduled.scheduleWithFixedDelay(() -> {
                try {
                    log.info("任务{}执行", sp.toString());
                    R r = sp.get();
                    if (!fun.apply(r)) {
                        log.info("任务{}执行完毕取消", sp.toString());
                        ScheduledFuture sf = station.get(sp);
                        sf.cancel(true);
                        station.remove(sp);
                    }
                } catch (Exception ex) {
                    ex.printStackTrace();
                    log.info("任务{}执行异常取消", sp.toString());
                    ScheduledFuture sf = station.get(sp);
                    sf.cancel(true);
                    station.remove(sp);
                    return;
                }
            }, delay.initDelay, delay.delay, delay.unit);
            station.put(sp, scheduledFuture);
        }
    }
}
